package core;

import annotation.DataLoaderClass;
import annotation.Param;
import annotation.SoiMethod;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import context.SoiContext;
import context.SoiHolder;
import context.SoiMethodHolder;
import enums.ExecuteStateEnum;
import enums.SOINodeTypeEnum;
import exception.SoiRuntimeException;
import load.SoiLoader;
import load.impl.DefaultSoiLoader;
import loader.DataLoader;
import model.*;
import org.codehaus.janino.ScriptEvaluator;
import util.AnnotationUtil;
import util.BeanUtil;
import util.StringUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @className: AbstractSOIContainer
 * @author: ocxz1
 * @date: 2025/3/12 下午5:11
 * @Version: 1.0
 * @description: soi
 */
public abstract class AbstractSOIContainer implements SOIContainer {

    private final Map<String, SoiNode> soiNodeMap = new ConcurrentHashMap<>();

    private final static SoiLoader soiLoader = new DefaultSoiLoader(SoiContext.class);

    /**
     * 开始编排执行
     *
     * @param chainKey   链路KEY
     * @param soiContext 上下文
     */
    @Override
    public void beginOrchestration(ChainKey chainKey, SoiContext soiContext) {
        SoiNode soiNode = soiNodeMap.get(chainKey.getKey());

        soiContext.setExecuteStateEnum(ExecuteStateEnum.NOT_STARTED);
        while (soiContext.getExecuteStateEnum().isNotTerminalState()) {
            if (soiNode == null) {
                throw new SoiRuntimeException("未找到节点：" + chainKey.getKey());
            }
            soiContext.setExecuteStateEnum(ExecuteStateEnum.EXECUTING);

            // 设置当前执行节点
            soiContext.setCurrentSoiNode(soiNode);
            ExecutionRecord executionRecord = soiContext.getAndPutExecutionRecord();

            // 执行单元执行
            if (evaluateCheckRule(soiNode, soiContext)) {
                try {
                    executeUnit(soiNode.getSoiUnit(), soiContext);
                } catch (Exception e) {
                    soiContext.getExecuteStateEnum().canTransitionTo(ExecuteStateEnum.FAILED);
                    executionRecord.setResultMsg(e.getMessage());

                    throw new SoiRuntimeException(e);
                }

                executionRecord.setExecuteState(soiContext.getExecuteStateEnum().code);
                executionRecord.setCompleteTime(new Date());
            }

            // 判断是否结束
            if (SOINodeTypeEnum.isEndNode(soiNode.getNodeType())) {
                soiContext.getExecuteStateEnum().canTransitionTo(ExecuteStateEnum.COMPLETED);
                return;
            }

            // 下一个服务节点
            for (int i = 0; i < soiNode.getNextNodes().size(); i++) {
                soiNode = soiNode.getNextNodes().get(i);
                if (evaluateCheckRule(soiNode, soiContext)) {
                    break;
                }
            }
        }
    }

    @Override
    public void register(ChainKey chainKey) {
        SoiNode soiNode = loadSoiNode(chainKey);
        soiNodeMap.remove(soiNode.getChineKey());
        soiNodeMap.put(soiNode.getChineKey(), soiNode);
    }

    @Override
    public void register(List<SoiNodeBean> soiNodeBeanList) {
        List<SoiNode> soiNodes = new ArrayList<>();
        for (SoiNodeBean soiNodeBean : soiNodeBeanList) {
            SoiNode soiNode = new SoiNode();
            soiNode.setId(soiNodeBean.getId());
            soiNode.setChineKey(soiNodeBean.getChainKey());
            soiNode.setNodeName(soiNodeBean.getNodeName());
            soiNode.setNodeCode(soiNodeBean.getNodeCode());
            soiNode.setNodeType(SOINodeTypeEnum.getNodeTypeName(soiNodeBean.getNodeType()));
            soiNode.setCheckRuleScript(soiNodeBean.getCheckRuleScript());

            // 设置参数
            if (soiNodeBean.getParamsJsonStr() != null && !soiNodeBean.getParamsJsonStr().isEmpty()) {
                soiNode.setParamsJson(JSONUtil.parseArray(soiNodeBean.getParamsJsonStr()));
            }
            SoiUnit soiUnit = new SoiUnit();
            soiUnit.setUnitIdentifier(soiNodeBean.getSoiUnitIdentifier());
            soiUnit.setUnitName(soiNodeBean.getSoiUnitName());
            soiNode.setSoiUnit(soiUnit);

            soiNodes.add(soiNode);
        }

        // 构建节点关系
        SoiNodeBean bean;
        SoiNode parentNode;
        for (SoiNode soiNode : soiNodes) {
            // 查找当前bean
            bean = BeanUtil.findById(soiNodeBeanList, soiNode.getId(), "未找到节点：");

            // 查找并且设置父节点
//            parentNode = BeanUtil.findById(soiNodes, bean.getParentId());
//            soiNode.setParentNode(parentNode);

            // 通过当前节点，找出子节点
            List<SoiNodeBean> list = BeanUtil.findList(soiNodeBeanList, soiNode.getId(), SoiNodeBean::getParentId);
            List<Long> childIds = list.stream().map(SoiNodeBean::getId).toList();
            List<SoiNode> childNodes = BeanUtil.findByIds(soiNodes, childIds);
            soiNode.setNextNodes(childNodes);
        }

        // 设置结束节点
        soiNodes.stream()
                .filter(soiNode -> soiNode.getNextNodes().isEmpty())
                .forEach(soiNode -> soiNode.setNodeType(SOINodeTypeEnum.END_NODE));

        // 找到根节点
        List<SoiNode> rootSoiNodes = soiNodes.stream()
                .filter(soiNode -> SOINodeTypeEnum.isStartNode(soiNode.getNodeType()))
                .toList();
        if (rootSoiNodes.size() != 1) {
            throw new SoiRuntimeException("根节点有且只能有一个");
        }
        SoiNode rootNode = rootSoiNodes.get(0);

        soiNodeMap.remove(rootNode.getChineKey());
        soiNodeMap.put(rootNode.getChineKey(), rootNode);
    }

    /**
     * 执行单元 执行
     *
     * @param soiUnit    执行单元
     * @param soiContext 上下文
     */
    public void executeUnit(SoiUnit soiUnit, SoiContext soiContext) {
        String unitIdentifier = soiUnit.getUnitIdentifier();
        ExecuteMethod executeMethod = SoiMethodHolder.getExecuteMethod(unitIdentifier);
        if (executeMethod == null) {
            return;
        }

        try {
            Method method = executeMethod.getMethod();
            Parameter[] parameters = method.getParameters();
            if (parameters.length == 0) {
                method.invoke(executeMethod.getBean());
                return;
            }
            Object[] args = new Object[parameters.length];

            SoiMethod soiMethod = executeMethod.getMethodAnnotation(SoiMethod.class);
            Class<? extends DataLoader> dataLoaderClass = soiMethod.dataLoader();
            DataLoader dataLoader = SoiHolder.getBean(dataLoaderClass);

            // 参数解析
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                if (parameter.getType().equals(SoiContext.class)) {
                    args[i] = soiContext;
                } else {
                    if (parameter.isAnnotationPresent(Param.class)) {
                        Param annotation = parameter.getAnnotation(Param.class);
                        String parameterName = annotation.value();
                        if (parameterName == null || parameterName.isEmpty()) {
                            parameterName = parameter.getType().getSimpleName();
                            parameterName = StringUtil.getFirstLowerCase(parameterName);
                        }
                        if (annotation.isAutoAssembly()) {
                            args[i] = dataLoader.loadData(soiContext, parameterName);
                        } else {
                            String methodKey = annotation.assemblyMethodKey();
                            if (methodKey == null || methodKey.isEmpty()) {
                                methodKey = parameter.getType().getSimpleName();
                                methodKey = StringUtil.generateGetMethodName(methodKey);
                                if (annotation.dataLoaderClass() != void.class) {
                                    methodKey = annotation.dataLoaderClass().getName() + "." + methodKey;
                                } else {
                                    DataLoaderClass methodDataLoaderClass = AnnotationUtil.getAnnotation(method, DataLoaderClass.class);
                                    if (methodDataLoaderClass != null && methodDataLoaderClass.value() != void.class) {
                                        methodKey = methodDataLoaderClass.value().getName() + "." + methodKey;
                                    } else {
                                        DataLoaderClass classDataLoaderClass = AnnotationUtil.getAnnotation(executeMethod.getBean().getClass(), DataLoaderClass.class);
                                        if (classDataLoaderClass != null && classDataLoaderClass.value() != void.class) {
                                            methodKey = classDataLoaderClass.value().getName() + "." + methodKey;
                                        }
                                    }
                                }
                            }
                            ExecuteMethod method1 = SoiMethodHolder.getExecuteMethod(methodKey);
                            Object invoke = method1.getMethod().invoke(method1.getBean(), soiContext);
                            args[i] = invoke;
                        }
                    } else {
                        args[i] = dataLoader.loadData(soiContext, parameter.getType());
                    }
                }
            }
            executeMethod.getMethod().invoke(executeMethod.getBean(), args);
        } catch (Exception e) {
            throw new SoiRuntimeException(e);
        }
    }

    public boolean evaluateCheckRule(SoiNode soiNode, SoiContext soiContext) {
        String checkRuleScript = soiNode.getCheckRuleScript();
        JSONArray paramsJson = soiNode.getParamsJson();

        if (checkRuleScript == null) {
            return true;
        } else {
            try {
                // 使用 Janino 执行代码片段
                ScriptEvaluator se = new ScriptEvaluator();
                se.setReturnType(boolean.class);

                String[] parameterNames =new String[paramsJson.size()];
                Class<?>[] parameterTypes = new Class[paramsJson.size()];
                Object[] parameters = new Object[paramsJson.size()];

                for (int i = 0; i < paramsJson.size(); i++) {
                    JSONObject paramJson = paramsJson.getJSONObject(i);
                    String parameterName = paramJson.getStr("name");
                    String parameterType = paramJson.getStr("type");
                    Class<?> paramClass = SoiMethodHolder.getClass(parameterType);
                    parameterNames[i] = parameterName;
                    parameterTypes[i] = paramClass;
                    parameters[i] = soiContext.getContextBeanByClass(paramClass);
                }

                // 设置参数类型和名称
                se.setParameters(parameterNames, parameterTypes);
                // 确保代码片段以 return 语句开头
                if (!checkRuleScript.trim().startsWith("return")) {
                    checkRuleScript = "return " + checkRuleScript;
                }
                if (!checkRuleScript.trim().endsWith(";")) {
                    checkRuleScript += ";";
                }

                se.cook(checkRuleScript);
                Object result = se.evaluate(parameters);
                return (boolean) result;
            } catch (Exception e) {
                throw new SoiRuntimeException(e);
            }
        }
    }
}
